package com.yaoxun.console.service;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import com.yaoxun.console.constant.CacheConstant;
import com.yaoxun.console.context.SubjectContextHolder;
import com.yaoxun.console.exception.ValidatorException;
import com.yaoxun.console.mapping.RouterMapping;
import com.yaoxun.console.xo.dto.ChannelMsg;
import com.yaoxun.console.xo.dto.LoginSession;
import com.yaoxun.console.xo.dto.Predicate;
import com.yaoxun.console.xo.dto.RouterDefinition;
import com.yaoxun.console.xo.req.RouterDeleteReq;
import com.yaoxun.console.xo.req.RouterEditReq;
import com.yaoxun.console.xo.req.RouterIdReq;
import com.yaoxun.console.xo.req.RouterListReq;
import com.yaoxun.console.xo.resp.PageInfo;
import com.yaoxun.console.xo.resp.RouterResp;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
public class RouterService {

	@Autowired
	private StringRedisTemplate stringRedisTemplate;

	@Autowired
	private NamespaceService namespaceService;
	
	@Autowired
	private JsonService jsonService;
	
	private static final String WEIGHT_NAME = "Weight";
	
	private String getRouterKey(String namespace) {
		return String.format(CacheConstant.ROUTERS_NAMESPACE, namespace);
	}
	
	private String getChannel(String namespace) {
		return String.format(CacheConstant.CHANNEL, namespace);
	}
	
	private void existOrderId(String namespace, String id) {
		String routerKey = getRouterKey(namespace);
		Boolean hasKey = stringRedisTemplate.opsForHash().hasKey(routerKey, id);
		if(hasKey) {
			throw new ValidatorException("id【"+id+"】已经存在");
		}
	}

	
	private void publish(String action, String namespace, RouterDefinition router) {
		
		String channel = getChannel(namespace);
		
		ChannelMsg channelMsg = new ChannelMsg(action, router);
		
		String msg = jsonService.toJsonString(channelMsg);
		
		stringRedisTemplate.convertAndSend(channel, msg);
		
	}
	
	
	public boolean getExistRouters(String namespace) {
		String routerKey = getRouterKey(namespace);
		return stringRedisTemplate.hasKey(routerKey);
	}
	
	public void add(RouterEditReq req) {
		
		namespaceService.checkExist(req.getNamespace());
		
		existOrderId(req.getNamespace(), req.getId());
		
		LoginSession session = SubjectContextHolder.getContext();
		
		RouterDefinition router = RouterMapping.INSTANCE.toRouterDefinition(req);
		
		String loginName = session.getLoginName();
		long timestamp = System.currentTimeMillis();
		router.setCreateLoginName(loginName)
			  .setModifyLoginName(loginName)
			  .setCreateTimestamp(timestamp)
			  .setModifyTimestamp(timestamp);
			  
		String json = jsonService.toJsonString(router);
		
		log.info("router loginName:{} add Json:{}", loginName, json);
		
		String routerKey = getRouterKey(req.getNamespace());
		
		stringRedisTemplate.opsForHash().put(routerKey, req.getId(), json);
		
		//发送pub
		publish(ChannelMsg.ADD, req.getNamespace(), router);
		
	}
	
	private void checkGroupNameEq(Predicate old, Predicate nw) {
		
		if(nw.getArgs() != null) {
			
			String groupName = nw.getArgs().get("_genkey_0");
			
			if(old != null) {
				
				String oldGroupName = old.getArgs().get("_genkey_0");
				
				if(!groupName.equals(oldGroupName)) {
					throw new ValidatorException("权重配置groupName不一致,如需修改请先重置为0");
				}
				
			}
			
		}
	}
	
	public void update(RouterEditReq req) {
		
		namespaceService.checkExist(req.getNamespace());
		
		String routerKey = getRouterKey(req.getNamespace());
		
		String json = (String) stringRedisTemplate.opsForHash().get(routerKey, req.getId());
		
		if(json == null) {
			throw new ValidatorException("命名空间【"+req.getNamespace()+"】中找不到id【"+req.getId()+"】");
		}
		
		RouterDefinition definition = jsonService.parse(json, RouterDefinition.class);
		
		Optional<Predicate> optional = req.getPredicates().stream().filter(e -> e.getName().equals(WEIGHT_NAME)).findAny();
		
		if(optional.isEmpty()) {
			
			boolean existOpenWeight = existOpenWeight(definition.getPredicates());
			
			if(existOpenWeight) {				
				throw new ValidatorException("请先将Weight数值重置为0在去除");
			}
			
		} else {
			
			Predicate nw = optional.get();
			
			Predicate old = getWeight(definition.getPredicates());
			
			checkGroupNameEq(old, nw);
			
		}
		
		LoginSession session = SubjectContextHolder.getContext();
		
		RouterDefinition router = RouterMapping.INSTANCE.toRouterDefinition(req);
		
		router.setModifyTimestamp(System.currentTimeMillis())
			  .setModifyLoginName(session.getLoginName())
			  .setCreateLoginName(definition.getCreateLoginName())
			  .setCreateTimestamp(definition.getCreateTimestamp());
		
		json = jsonService.toJsonString(router);
		
		stringRedisTemplate.opsForHash().put(routerKey, req.getId(), json);
		
		log.info("router loginName:{} update Json:{}", session.getLoginName(), json);
		
		//发送pub
		publish(ChannelMsg.UPDATE, req.getNamespace(), router);
		
	}
	
	
	private Predicate getWeight(List<Predicate> predicates) {
		
		Optional<Predicate> optional = predicates.stream().filter(e -> {
			
			if(e.getName().equals(WEIGHT_NAME) && e.getArgs() != null) {
				
				String num = e.getArgs().get("_genkey_1");
				
				if(num != null) {
					
					if(NumberUtils.toInt(num) > 0) {
						return true;
					}
					
				}
			}
			
			return false;
			
		}).findAny();
		
		return optional.orElse(null);
		
	}
	
	
	private boolean existOpenWeight(List<Predicate> predicates) {
		
		Predicate weight = getWeight(predicates);
		
		if(weight == null) {
			return false;
		}
		
		return true;
	}
	
	
	public void delete(RouterDeleteReq req) {
		
		namespaceService.checkExist(req.getNamespace());
		
		String routerKey = getRouterKey(req.getNamespace());
		
		String json = (String) stringRedisTemplate.opsForHash().get(routerKey, req.getId());
		
		if(json == null) {
			throw new ValidatorException("命名空间【"+req.getNamespace()+"】中找不到id【"+req.getId()+"】");
		}
		
		RouterDefinition router = jsonService.parse(json, RouterDefinition.class);
		
		boolean existOpenWeight = existOpenWeight(router.getPredicates());
		
		if(existOpenWeight) {
			throw new ValidatorException("请先将Weight数值重置为0在删除");
		}
		
		LoginSession session = SubjectContextHolder.getContext();
		
		stringRedisTemplate.opsForHash().delete(routerKey, req.getId());
		
		log.info("router loginName:{} delete json:{}", session.getLoginName(), json);
		
		//发送pub
		publish(ChannelMsg.DEL, req.getNamespace(), router);
		
	}

	public PageInfo<RouterResp> list(RouterListReq req) {
		
		PageInfo<RouterResp> pageInfo = new PageInfo<>();
		
		String routerKey = getRouterKey(req.getNamespace());
		
		List<Object> values = stringRedisTemplate.opsForHash().values(routerKey);
		
		if(CollectionUtils.isEmpty(values)) {
			return pageInfo;
		}
		
		List<RouterResp> records = new ArrayList<>();
		
		values.forEach(e -> {
			
			RouterResp resp = jsonService.parse(e.toString(), RouterResp.class);
			
			resp.setNamespace(req.getNamespace());
			
			Date createTime = new Date(resp.getCreateTimestamp());
			
			resp.setCreateTime(createTime);
			
			records.add(resp);
			
		});
		
		List<RouterResp> newRecords = records.stream()
					.sorted(Comparator.comparing(RouterResp::getCreateTimestamp).reversed())
					.collect(Collectors.toList());
		
		pageInfo.setRecords(newRecords);
		
		return pageInfo;
	}

	public RouterResp get(RouterIdReq req) {
		
		String routerKey = getRouterKey(req.getNamespace());
		
		String json = (String) stringRedisTemplate.opsForHash().get(routerKey, req.getId());
		
		if(json == null) {
			return null;
		}
		
		return jsonService.parse(json, RouterResp.class);
		
	}

}
